package com.bestcem.xm.deliver.service.impl;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bestcem.xm.common.core.exception.BusinessException;
import com.bestcem.xm.common.core.oss.enums.OssPathEnum;
import com.bestcem.xm.component.mybatis.service.impl.XmServiceImpl;
import com.bestcem.xm.componet.filestorage.service.StorageService;
import com.bestcem.xm.componet.redis.service.RedisService;
import com.bestcem.xm.deliver.controller.v2.param.CreateEmbedParam;
import com.bestcem.xm.deliver.controller.v2.param.EditEmbedParam;
import com.bestcem.xm.deliver.entity.DeliverDeliver;
import com.bestcem.xm.deliver.entity.DeliverEmbedDeliver;
import com.bestcem.xm.deliver.entity.DeliverSurveyDeliver;
import com.bestcem.xm.deliver.enums.DeliverTtypeEnum;
import com.bestcem.xm.deliver.grpc.client.UserGrpcClient;
import com.bestcem.xm.deliver.grpc.client.dto.OrgInfoDto;
import com.bestcem.xm.deliver.mapper.DeliverDeliverMapper;
import com.bestcem.xm.deliver.mapper.DeliverEmbedDeliverMapper;
import com.bestcem.xm.deliver.mapper.DeliverSurveyDeliverMapper;
import com.bestcem.xm.deliver.service.DeliverDeliverService;
import com.bestcem.xm.deliver.service.DeliverEmbedDeliverService;
import com.bestcem.xm.deliver.service.ServiceSequenceService;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.bestcem.xm.deliver.enums.DeliverChannelEnum.DELIVER_CHANNEL_EMBED;
import static com.bestcem.xm.deliver.enums.DeliverScenesEnum.DELIVER_SCENES_EMBED;
import static com.bestcem.xm.deliver.enums.DeliverStatusEnum.*;
import static com.bestcem.xm.deliver.enums.DeliverTtypeEnum.DELIVER_TTYPE_EMBED;
import static com.bestcem.xm.deliver.enums.EmbedDeliverCTypeEnum.EMBEDDELIVER_C_TYPE_MOBILE;
import static com.bestcem.xm.deliver.enums.EmbedDeliverCTypeEnum.EMBEDDELIVER_C_TYPE_PC;
import static com.bestcem.xm.deliver.enums.EmbedDeliverStatusEnum.EMBEDDELIVER_STATUS_NOT_RELEASE;
import static com.bestcem.xm.deliver.enums.EmbedDeliverStatusEnum.EMBEDDELIVER_STATUS_RELEASE;
import static com.bestcem.xm.deliver.grpc.v1.services.EmbedDeliverIsRelease.EMBEDDELIVER_IS_RELEASE_DRAFT;
import static com.bestcem.xm.deliver.grpc.v1.services.EmbedDeliverIsRelease.EMBEDDELIVER_IS_RELEASE_RELEASE;
import static com.bestcem.xm.survey.grpc.v1.services.RespondentSource.RESPONDENT_SOURCE_EMBED;

/**
 * @author been
 * @description 针对表【ds_deliver_embeddeliver】的数据库操作Service实现
 * @createDate 2022-11-07 13:49:46
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DeliverEmbedDeliverServiceImpl extends XmServiceImpl<DeliverEmbedDeliverMapper, DeliverEmbedDeliver>
        implements DeliverEmbedDeliverService {
    private static final String SEPARATOR = "/";
    private static final String ROOT_FILE_PATH = System.getProperty("user.dir");

    @Value("${app.deliver.survey.system.url}")
    private String surveySystemUrl;

    private @NonNull ServiceSequenceService serviceSequenceService;
    private @NonNull DeliverDeliverMapper deliverDeliverMapper;
    private @NonNull RedisService redisService;
    private @NonNull DeliverSurveyDeliverMapper deliverSurveyDeliverMapper;
    private @NonNull UserGrpcClient userGrpcClient;
    private @NonNull DeliverDeliverService deliverDeliverService;
    //private @NonNull OssService ossService;
    private @NonNull StorageService storageService;

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String createEmbedDeliver(CreateEmbedParam param) {
        if (param.getPcConfig() == null) {
            throw new BusinessException("pc_config missing");
        }
        if (param.getMobileConfig() == null) {
            throw new BusinessException("mobile_config missing");
        }

        DeliverDeliver deliver = new DeliverDeliver();
        deliver.setName(param.getDeliverName());
        deliver.setCreatedId(param.getUserId());
        deliver.setTtype(DELIVER_TTYPE_EMBED.getValue());
        deliver.setChannel(DELIVER_CHANNEL_EMBED.getValue());
        deliver.setScenes(DELIVER_SCENES_EMBED.getValue());
        deliver.setStatus(DELIVER_STATUS_START.getValue());
        deliver.setStarttime(LocalDateTime.now());
        // 获取当前时间戳，单位微秒
        deliver.setVersion(System.currentTimeMillis() * 1000);
        // 设置code
        deliver.setCode(String.valueOf(serviceSequenceService.getSequence("DELIVER", 1)));
        // 设置默认值
        deliver.setOrgId(param.getOrgId());
        deliver.setDisable(0);
        deliver.setDelayTime("[]");
        deliver.setDelayFailedTime("{}");
        deliver.setEnableRespondent(1);
        deliver.setAwardUseType(0);
        deliverDeliverMapper.insert(deliver);
        String deliverId = deliver.getId();

        setDeliverRespondentEnableRedis(deliverId, true);
        deliverCreateSurveydeliver(deliverId, param.getSurveyId(), param.getOrgId(), param.getUserId());

        OrgInfoDto org = userGrpcClient.getOrganization(param.getOrgId());
        // 获得问卷链接
        String surveyLink = getSurveyEmbedDwzUrl(param.getOrgId(), org, param.getSurveyId(), deliverId, new HashMap<>());
        String previewUrl = getCorpUrl(null);
        long releaseVersion = System.currentTimeMillis();
        param.getPcConfig().put("perview_url", previewUrl);
        param.getPcConfig().put("release_version", releaseVersion);
        param.getPcConfig().put("status", EMBEDDELIVER_STATUS_RELEASE.getValue());
        param.getMobileConfig().put("perview_url", previewUrl);
        param.getMobileConfig().put("release_version", releaseVersion);
        param.getMobileConfig().put("status", EMBEDDELIVER_STATUS_RELEASE.getValue());
        // 创建草稿配置数据
        String draftEmbedId = createEmbeddeliver(param, surveyLink, deliverId, EMBEDDELIVER_IS_RELEASE_DRAFT.getNumber());
        // 创建发布配置数据
        String releaseEmbedId = createEmbeddeliver(param, surveyLink, deliverId, EMBEDDELIVER_IS_RELEASE_RELEASE.getNumber());
        // 修改默认序号
        deliverDeliverService.doDefaultDeliverNameSeq(param.getDeliverName(), param.getSurveyId(), DeliverTtypeEnum.DELIVER_TTYPE_EMBED.getValue());

        // 获取org_code组织编号
        String orgCode = org.getCode();
        // 创建更新配置里埋年创建的code
        String embedCode = genTransitionJs(deliverId);
        LambdaUpdateWrapper<DeliverEmbedDeliver> updateWrapper = new LambdaUpdateWrapper<>();
        //构造查询条件-更新
        List<String> ids = Arrays.asList(draftEmbedId, releaseEmbedId);
        updateWrapper.set(DeliverEmbedDeliver::getEmbedCode, embedCode)
                .set(DeliverEmbedDeliver::getUpdatedDt, LocalDateTime.now())
                .in(DeliverEmbedDeliver::getId, ids);
        baseMapper.update(null, updateWrapper);
        log.info("成功创建嵌入投放");
        return deliverId;
    }

    public String saveJs(String content, String tmpPath, String ossPath, String name) {
        File file1 = new File(tmpPath);
        if (!file1.exists()) {
            file1.mkdirs();
        }
        Path path = Paths.get(tmpPath, name);
        // 使用newBufferedWriter创建文件并写文件
        // 这里使用了try-with-resources方法来关闭流，不用手动关闭
        try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
            writer.write(content);
        } catch (IOException e) {
            log.error("error to create file, e = ", e);
        }
        ossPath = StrUtil.addSuffixIfNot(ossPath, SEPARATOR);
        File fileInfo = new File(tmpPath, name);
        //String ossFilePath = ossService.uploadSimpleFile2(fileInfo, name, ossPath, 0, null, "", OssPathEnum.DELIVER);
        String ossFilePath = storageService.upload(fileInfo, name, ossPath, new Dict()).getUrl();
        fileInfo.delete();
        return ossFilePath;
    }

    @Override
    public void triggerDeliverStatus(DeliverDeliver deliver, String action) {
        if (StrUtil.equals("start", action)) {
            deliverDeliverMapper.update(null,
                    new UpdateWrapper<DeliverDeliver>().lambda()
                            .eq(DeliverDeliver::getId, deliver.getId())
                            .set(DeliverDeliver::getStatus, DELIVER_STATUS_START.getValue())
                            .set(DeliverDeliver::getDeliverTime, LocalDateTime.now()));
            releaseEmbedDeliver(deliver.getId(), EMBEDDELIVER_C_TYPE_PC.getValue());
            releaseEmbedDeliver(deliver.getId(), EMBEDDELIVER_C_TYPE_MOBILE.getValue());
        } else if (StrUtil.equals("finish", action)) {
            deliverDeliverMapper.update(null,
                    new UpdateWrapper<DeliverDeliver>().lambda()
                            .eq(DeliverDeliver::getId, deliver.getId())
                            .set(DeliverDeliver::getStatus, DELIVER_STATUS_FINISH.getValue())
                            .set(DeliverDeliver::getDeliverTime, LocalDateTime.now()));
            genEmbedJs(deliver.getId());
        } else if (StrUtil.equals("delete", action)) {
            deliverDeliverMapper.update(null,
                    new UpdateWrapper<DeliverDeliver>().lambda()
                            .eq(DeliverDeliver::getId, deliver.getId())
                            .set(DeliverDeliver::getStatus, DELIVER_STATUS_DELETE.getValue())
                            .set(DeliverDeliver::getDeliverTime, LocalDateTime.now()));
            genEmbedJs(deliver.getId());
        }
    }

    @Override
    public String genEmbedJs(String deliverId) {
        // 生成js文件
        LambdaQueryWrapper<DeliverEmbedDeliver> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeliverEmbedDeliver::getDeliverId, deliverId)
                .eq(DeliverEmbedDeliver::getIsRelease, EMBEDDELIVER_IS_RELEASE_RELEASE.getNumber())
                .last("limit 1");
        DeliverEmbedDeliver embedDeliver = baseMapper.selectOne(queryWrapper);
        DeliverDeliver deliver = deliverDeliverMapper.selectById(deliverId);

        // 从前端获取js模板
        String content = getTemplate();
        // 拼接参数，生成js
        content += "\n" + "embed_deliver.init("
                + embedDeliver.getPcConfig().replace("True", "true").replace("False", "false") + ","
                + embedDeliver.getMobileConfig().replace("True", "true").replace("False", "false") + ","
                + "\"" + embedDeliver.getSurveyLink() + "\","
                + "\"" + deliver.getId() + "\","
                + deliver.getStatus() + ")";

        String tmpPath = ROOT_FILE_PATH + File.separator + StrUtil.replace(OssPathEnum.TEMP_DELIVER.getPath(), "/", File.separator);
        String ossPath = OssPathEnum.DELIVER.getPath() + "embed" + SEPARATOR + deliverId;
        String name = "embed_deliver.js";
        return saveJs(content, tmpPath, ossPath, name);
    }

    @Override
    public DeliverEmbedDeliver getEmbedDeliver(String deliverId) {
        if (StringUtils.isEmpty(deliverId)) {
            throw new BusinessException("deliver_id is required");
        }
        LambdaQueryWrapper<DeliverEmbedDeliver> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeliverEmbedDeliver::getDeliverId, deliverId)
                .eq(DeliverEmbedDeliver::getIsRelease, EMBEDDELIVER_IS_RELEASE_DRAFT.getNumber())
                .last("limit 1");
        DeliverEmbedDeliver embedDeliver = baseMapper.selectOne(queryWrapper);
        if (embedDeliver == null) {
            throw new BusinessException("embedDeliver is null");
        }
        String embedCode = embedDeliver.getEmbedCode();
        // 文件名固定的，无需强制转义
        embedDeliver.setEmbedCode("<script type=\"text/javascript\" src=" + embedCode + "></script>");
        return embedDeliver;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public JSONObject editEmbedDeliver(String id, EditEmbedParam param) {
        if (StringUtils.isEmpty(id)) {
            throw new BusinessException("url must endswith id");
        }
        Integer cType = param.getCType();
        DeliverEmbedDeliver embedDeliver = baseMapper.selectById(id);
        if (embedDeliver == null) {
            throw new BusinessException("id is not found");
        }
        if (EMBEDDELIVER_C_TYPE_PC.getValue() != cType && EMBEDDELIVER_C_TYPE_MOBILE.getValue() != cType) {
            throw new BusinessException("c_type is not found");
        }
        // 设置为未发布
        Long releaseVersion = System.currentTimeMillis();
        //构造查询条件-更新
        LambdaUpdateWrapper<DeliverEmbedDeliver> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(DeliverEmbedDeliver::getUpdatedDt, LocalDateTime.now());
        if (EMBEDDELIVER_C_TYPE_PC.getValue() == cType) {
            param.getPcConfig().put("status", EMBEDDELIVER_STATUS_NOT_RELEASE.getValue());
            param.getPcConfig().put("release_version", releaseVersion);
            if (param.getPcConfig().get("status") instanceof Boolean) {
                param.getPcConfig().put("status", param.getPcConfig().getBoolean("status") ? 1 : 0);
            }
            updateWrapper.set(DeliverEmbedDeliver::getPcConfig, JSONObject.toJSONString(param.getPcConfig()));
        } else {
            param.getMobileConfig().put("status", EMBEDDELIVER_STATUS_NOT_RELEASE.getValue());
            param.getMobileConfig().put("release_version", releaseVersion);
            if (param.getMobileConfig().get("status") instanceof Boolean) {
                param.getMobileConfig().put("status", param.getMobileConfig().getBoolean("status") ? 1 : 0);
            }
            updateWrapper.set(DeliverEmbedDeliver::getMobileConfig, JSONObject.toJSONString(param.getMobileConfig()));
        }
        updateWrapper.eq(DeliverEmbedDeliver::getId, id);
        baseMapper.update(null, updateWrapper);
        JSONObject deJs = new JSONObject();
        deJs.put("id", id);
        deJs.put("release_version", releaseVersion);
        return deJs;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void releaseEmbedDeliver(String deliverId, Integer cType) {
        LambdaQueryWrapper<DeliverEmbedDeliver> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(DeliverEmbedDeliver::getDeliverId, deliverId)
                .eq(DeliverEmbedDeliver::getIsRelease, EMBEDDELIVER_IS_RELEASE_DRAFT.getNumber())
                .last("limit 1");
        DeliverEmbedDeliver embedDeliverDraft = baseMapper.selectOne(queryWrapper);
        if (embedDeliverDraft == null) {
            throw new BusinessException("data no found");
        }
        // 更新 release 版本数据
        //构造查询条件-更新
        LambdaUpdateWrapper<DeliverEmbedDeliver> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(DeliverEmbedDeliver::getUpdatedDt, LocalDateTime.now());
        if (StringUtils.isNotEmpty(embedDeliverDraft.getOrgId())) {
            updateWrapper.set(DeliverEmbedDeliver::getOrgId, embedDeliverDraft.getOrgId());
        }
        if (StringUtils.isNotEmpty(embedDeliverDraft.getDeliverId())) {
            updateWrapper.set(DeliverEmbedDeliver::getDeliverId, embedDeliverDraft.getDeliverId());
        }
        if (StringUtils.isNotEmpty(embedDeliverDraft.getSurveyId())) {
            updateWrapper.set(DeliverEmbedDeliver::getSurveyId, embedDeliverDraft.getSurveyId());
        }
        if (StringUtils.isNotEmpty(embedDeliverDraft.getSurveyLink())) {
            updateWrapper.set(DeliverEmbedDeliver::getSurveyLink, embedDeliverDraft.getSurveyLink());
        }
        if (StringUtils.isNotEmpty(embedDeliverDraft.getEmbedCode())) {
            updateWrapper.set(DeliverEmbedDeliver::getEmbedCode, embedDeliverDraft.getEmbedCode());
        }
        if (embedDeliverDraft.getIsRelease() != null) {
            updateWrapper.set(DeliverEmbedDeliver::getIsRelease, EMBEDDELIVER_IS_RELEASE_RELEASE.getNumber());
        }

        // 设置为已发布
        LambdaUpdateWrapper<DeliverEmbedDeliver> draftWrapper = new LambdaUpdateWrapper<>();
        draftWrapper.set(DeliverEmbedDeliver::getUpdatedDt, LocalDateTime.now());
        if (EMBEDDELIVER_C_TYPE_PC.getValue() == cType) {
            updateWrapper.set(DeliverEmbedDeliver::getPcConfig, embedDeliverDraft.getPcConfig());
            JSONObject pcJson = JSON.parseObject(embedDeliverDraft.getPcConfig());
            pcJson.put("status", EMBEDDELIVER_STATUS_RELEASE.getValue());
            draftWrapper.set(DeliverEmbedDeliver::getPcConfig, pcJson.toJSONString());
        } else {
            updateWrapper.set(DeliverEmbedDeliver::getMobileConfig, embedDeliverDraft.getMobileConfig());
            JSONObject mobileJson = JSON.parseObject(embedDeliverDraft.getMobileConfig());
            mobileJson.put("status", EMBEDDELIVER_STATUS_RELEASE.getValue());
            draftWrapper.set(DeliverEmbedDeliver::getMobileConfig, mobileJson.toJSONString());
        }
        updateWrapper.eq(DeliverEmbedDeliver::getDeliverId, deliverId)
                .eq(DeliverEmbedDeliver::getIsRelease, EMBEDDELIVER_IS_RELEASE_RELEASE.getNumber());
        baseMapper.update(null, updateWrapper);
        // 生成js文件
        genEmbedJs(deliverId);
        // 更新草稿数据为已发布
        draftWrapper.eq(DeliverEmbedDeliver::getId, embedDeliverDraft.getId());
        baseMapper.update(null, draftWrapper);
    }

    private String createEmbeddeliver(CreateEmbedParam param, String surveyLink, String deliverId, Integer isRelease) {
        DeliverEmbedDeliver embedDeliver = new DeliverEmbedDeliver();
        embedDeliver.setDeliverId(deliverId);
        embedDeliver.setSurveyId(param.getSurveyId());
        embedDeliver.setSurveyLink(surveyLink);
        embedDeliver.setMobileConfig(JSONObject.toJSONString(param.getMobileConfig()));
        embedDeliver.setPcConfig(JSONObject.toJSONString(param.getPcConfig()));
        embedDeliver.setIsRelease(isRelease);
        baseMapper.insert(embedDeliver);
        return embedDeliver.getId();
    }

    private void setDeliverRespondentEnableRedis(String deliverId, boolean enableRespondent) {
        // 设置是容许答题到redis中
        String key = "deliver:deliver_enable_respondent" + deliverId;
        // 创建投放时默认是容许答题
        // 设置redis 值为”1“ 容许答题 值为”0“ 不容许答题
        redisService.set(key, enableRespondent ? "1" : "0");
    }

    private void deliverCreateSurveydeliver(String deliverId, String surveyId, String orgId, String userId) {
        // 为了发更新消息project_updated，做了一层封装
        DeliverSurveyDeliver surveyDeliver = new DeliverSurveyDeliver();
        surveyDeliver.setDeliverId(deliverId);
        surveyDeliver.setSurveyId(surveyId);
        surveyDeliver.setOrgId(orgId);
        surveyDeliver.setCreatedId(userId);
        deliverSurveyDeliverMapper.insert(surveyDeliver);
        // python有发送mq
        //     if params.get("survey_id"):
        //        await redis_mq.publish(topics.project_updated, {'project_id': params.get("survey_id")})
    }

    private String getSurveyEmbedDwzUrl(String orgId, OrgInfoDto org, String surveyId, String deliverId, Map<String, String> kwargs) {
        kwargs.put("source", String.valueOf(RESPONDENT_SOURCE_EMBED.getNumber()));
        kwargs.put("scenes", String.valueOf(DELIVER_SCENES_EMBED.getValue()));
        JSONObject surveyLink = deliverDeliverService.getSurveyLink(orgId, surveyId, deliverId, org.getCode(), kwargs);
        String url = surveyLink.getString("url");
        String domain = surveyLink.getString("domain");
        return deliverDeliverService.getSurveyDwzLink(url, domain);
    }

    /**
     * 私有化固定
     */
    private String getCorpUrl(String orgCode) {
        return surveySystemUrl;
    }

    private String genTransitionJs(String deliverId) {
        // 生成过度js文件
        String url = genEmbedJs(deliverId);
        // 渲染操作
        // 读取模板内容
        //String path = Objects.requireNonNull(this.getClass().getClassLoader().getResource("")).getPath();
        //String allPath = path + "file" + SEPARATOR + "template" + SEPARATOR + "transition_template.js";
        //// 转换成List<String>, 要注意java.lang.OutOfMemoryError: Java heap space
        //List<String> lines = new ArrayList<>();
        //try {
        //    lines = Files.readAllLines(Paths.get(allPath), StandardCharsets.UTF_8);
        //} catch (IOException e) {
        //    log.error("Now read the file error, file = {}", allPath);
        //}
        //String content;
        //StringBuilder contentBuilder = new StringBuilder();
        //for (String line : lines) {
        //    contentBuilder.append(line);
        //}
        //content = contentBuilder.toString();

        //我们的文件存放在src/resources/config目录下：
        //ResourceUtil.readUtf8Str("config/test.xml");
        String content = ResourceUtil.readUtf8Str("file/template/transition_template.js");
        content = StrUtil.replace(content, "{{url}}", url);
        content = StrUtil.replace(content, "{{deliver_id}}", deliverId);

        String tmpPath = ROOT_FILE_PATH + File.separator + StrUtil.replace(OssPathEnum.TEMP_DELIVER.getPath(), "/", File.separator);
        String ossPath = OssPathEnum.DELIVER.getPath() + "embed" + SEPARATOR + deliverId;
        String name = "transition.js";
        return saveJs(content, tmpPath, ossPath, name);
    }

    private String getTemplate() {
        return ResourceUtil.readUtf8Str("file/template/embed_deliver.js");
    }
}